#include "draggermanipulator.h"
#include "qsignalslotcenter.h"
#include "pickmanager.h"
#include <osgViewer\View>

namespace ControlCenter
{
	DraggerCallbackExt::DraggerCallbackExt():
	osgManipulator::DraggerTransformCallback(NULL,osgManipulator::DraggerTransformCallback::HANDLE_ALL)
	{}

	DraggerCallbackExt::DraggerCallbackExt(osg::MatrixTransform* transform, int handleCommandMask):
	osgManipulator::DraggerTransformCallback(transform,handleCommandMask)
	{}

	DraggerCallbackExt::~DraggerCallbackExt()
	{}

	bool DraggerCallbackExt::receive(const osgManipulator::MotionCommand& mc)
	{
		osgManipulator::DraggerTransformCallback::receive(mc);
		switch (mc.getStage())
		{
		case osgManipulator::MotionCommand::FINISH:
			{
				CommandObject newobj;
				newobj._transform = _transform.get();
				newobj._matrix = _transform->getMatrix();
				_commandVector.push_back(newobj);
				_redoVector.clear();
			}
			break;
		default:
			return false;
		}
	}

	void DraggerCallbackExt::setTransform(osg::MatrixTransform* transform)
	{
		if(_transform!=transform)
		{
			_transform = transform;
			CommandObject newobj;
			newobj._transform = _transform.get();
			newobj._matrix = _transform->getMatrix();
			_commandVector.push_back(newobj);
		}
	}

	void DraggerCallbackExt::setHandleCommandMask(int cm)
	{
		_handleCommandMask = cm;
	}

	void DraggerCallbackExt::redo()
	{
		if(_redoVector.size()>0)
		{
			_redoVector.at(_redoVector.size()-1)._transform->setMatrix(_redoVector.at(_redoVector.size()-1)._matrix);
			_commandVector.push_back(_redoVector.at(_redoVector.size()-1));
			_redoVector.pop_back();
		}
	}

	void DraggerCallbackExt::undo()
	{
		if(_commandVector.size()>1)
		{
			_redoVector.push_back(_commandVector.at(_commandVector.size()-1));
			_commandVector.at(_commandVector.size()-2)._transform->setMatrix(_commandVector.at(_commandVector.size()-2)._matrix);
			_commandVector.pop_back();
		}
	}

	void DraggerCallbackExt::deleteTransform(osg::MatrixTransform* transform)
	{
		int i=0;
		while(i<_commandVector.size())
		{
			if(_commandVector.at(i)._transform == transform)
				_commandVector.remove(i);
			else
				i++;
		}
		i=0;
		while(i<_redoVector.size())
		{
			if(_redoVector.at(i)._transform == transform)
				_redoVector.remove(i);
			else
				i++;
		}
	}

	DraggerManipulator::DraggerManipulator()
	{
		_usePickManager = false;
		_action = ACTION_OPERATE_NORMAL;
		_translateDragger = new osgManipulator::TranslateAxisDragger();
		_translateDragger->setupDefaultGeometry();
		_translateDragger->setNodeMask(NODE_MASK_NOSELECT);

		_rotateDragger = new osgManipulator::TrackballDragger();
		_rotateDragger->setupDefaultGeometry();
		_rotateDragger->setNodeMask(NODE_MASK_NOSELECT);

		_draggerCallbackExt = new DraggerCallbackExt;
	}

	DraggerManipulator::~DraggerManipulator()
	{}

	void DraggerManipulator::mouseMove(const osgGA::GUIEventAdapter* ea0,const osgGA::GUIEventAdapter* ea1)
	{}

	void DraggerManipulator::mouseClick(const osgGA::GUIEventAdapter* ea0,const osgGA::GUIEventAdapter* ea1,
		const osgGA::GUIEventAdapter* key0,const osgGA::GUIEventAdapter* key1)
	{
		float f1 = ea0->getX();
		float f2 = ea0->getY();
		if(ea0->getButtonMask() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
		{
			QVector<osg::MatrixTransform*> vec = pickObject(osg::Vec2(f1,f2),NODE_MASK_SELECT);
			if(vec.size() == 1)
			{
				if( _operateObjectVector.size() == 1)
				{
					if(vec.at(0)!=_operateObjectVector.at(0))
					{
						hideDragger();
						_operateObjectVector.clear();
						_operateObjectVector = vec;
						showDragger();
					}
				}
				else
				{
					_operateObjectVector = vec;
					showDragger();
				}
			}
		}
	}

	void DraggerManipulator::keyDown(const osgGA::GUIEventAdapter* ea0,osgGA::GUIActionAdapter& us)
	{}

	void DraggerManipulator::mouseRelease(const osgGA::GUIEventAdapter* ea0,const osgGA::GUIEventAdapter* ea1,
		const osgGA::GUIEventAdapter* key0,const osgGA::GUIEventAdapter* key1)
	{}

	void DraggerManipulator::enableManipulator()
	{}

	void DraggerManipulator::disenableManipulator()
	{}

	void DraggerManipulator::showDragger()
	{
		switch(_action)
		{
		case ACTION_OPERATE_NORMAL:
			hideDragger();
			break;
		case ACTION_OPERATE_TRANSLATE:
			if(_operateObjectVector.size()==1)
			{
				_draggerCallbackExt->setTransform(_operateObjectVector.at(0));
				_draggerCallbackExt->setHandleCommandMask(osgManipulator::DraggerTransformCallback::HANDLE_TRANSLATE_IN_LINE);
				_translateDragger->addDraggerCallback(_draggerCallbackExt);
				osg::Group* parentnode =  _operateObjectVector.at(0)->getParent(0);
				parentnode->addChild(_translateDragger);
				float scale = _operateObjectVector.at(0)->getBound().radius();
				_translateDragger->setMatrix(osg::Matrix::scale(scale, scale, scale) *_operateObjectVector.at(0)->getMatrix());
				_translateDragger->setHandleEvents(true);
				_translateDragger->setActivationMouseButtonMask(osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON);
			}
			break;
		case ACTION_OPERATE_ROTATE:
			{
				if(_operateObjectVector.size()==1)
				{
					_draggerCallbackExt->setTransform(_operateObjectVector.at(0));
					_draggerCallbackExt->setHandleCommandMask(osgManipulator::DraggerTransformCallback::HANDLE_ROTATE_3D);
					_rotateDragger->addDraggerCallback(_draggerCallbackExt);
					osg::Group* parentnode =  _operateObjectVector.at(0)->getParent(0);
					parentnode->addChild(_rotateDragger);
					float scale = _operateObjectVector.at(0)->getBound().radius();
					_rotateDragger->setMatrix(osg::Matrix::scale(scale, scale, scale) *_operateObjectVector.at(0)->getMatrix());
					_rotateDragger->setHandleEvents(true);
					_translateDragger->setActivationMouseButtonMask(osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON);
				}
			}
			break;
		default:
			break;
		};
	}

	void DraggerManipulator::hideDragger()
	{
		if(_operateObjectVector.size() == 1)
		{
			if(_translateDragger->getParents().size()>0)
			{
				osg::Group* parentnode = _translateDragger->getParent(0);
				if(parentnode!=NULL)
				{
					parentnode->removeChild(_translateDragger);
					//_translateDragger->removeTransformUpdating(_operateObjectVector.at(0));
					_translateDragger->removeDraggerCallback(_draggerCallbackExt);
				}
			}
			if(_rotateDragger->getParents().size()>0)
			{
				osg::Group* parentnode = _rotateDragger->getParent(0);
				if(parentnode!=NULL)
				{
					parentnode->removeChild(_rotateDragger);
					//_rotateDragger->removeTransformUpdating(_operateObjectVector.at(0));
					_rotateDragger->removeDraggerCallback(_draggerCallbackExt);
				}
			}
		}
	}

	void DraggerManipulator::setToolAction(ActionData* action)
	{
		ManipulatorBase::setToolAction(action);
		switch(_action)
		{
		case ACTION_OPERATE_NORMAL:
		case ACTION_OPERATE_TRANSLATE:
		case ACTION_OPERATE_ROTATE:
			hideDragger();
			showDragger();
			break;
		case ACTION_UNDO_EVENT:
			_draggerCallbackExt->undo();
			break;
		case ACTION_REDO_EVENT:
			_draggerCallbackExt->redo();
			break;
		case ACTION_DELETE_DEVICE:
			_draggerCallbackExt->deleteTransform(_operateObjectVector.at(0));
			break;
		case ACTION_OPERATE_RESTORE:
			{
				hideDragger();
				_operateObjectVector.at(0)->setMatrix(osg::Matrix());
			}
			break;
		default:
			break;
		}
	}

	void DraggerManipulator::setOperateObject(osg::MatrixTransform* mt)
	{
		_operateObjectVector.clear();
		_operateObjectVector.push_back(mt);
	}

	QVector<osg::MatrixTransform*> DraggerManipulator::pickObject(const osg::Vec2 &mousepos,unsigned int traversalmask)
	{
		QVector<osg::MatrixTransform*> v;
		bool findsuccess = false;
		osgUtil::LineSegmentIntersector::Intersections hitlist;
		_view->computeIntersections(mousepos.x(), mousepos.y(),hitlist, traversalmask);
		for (std::multiset<osgUtil::LineSegmentIntersector::Intersection>::const_iterator hitItr = hitlist.begin();
			hitItr != hitlist.end();++hitItr)
		{
			for (osg::NodePath::const_reverse_iterator nodeItr = hitItr->nodePath.rbegin();
				nodeItr != hitItr->nodePath.rend();
				++nodeItr)
			{
				if (dynamic_cast<osg::MatrixTransform*>(*nodeItr)!=NULL && (*nodeItr)->getNodeMask() == traversalmask)
				{
					v.push_back(dynamic_cast<osg::MatrixTransform*>(*nodeItr));
					findsuccess = true;
					break;
				}
				if(findsuccess)
					break;
			}
			if(findsuccess)
				break;
		}
		return v;
	}
}